# Import Markup for rendering safe HTML.
from flask import Markup
# Import the base Model class and the 'renders' decorator from Flask-AppBuilder.
from flask_appbuilder import Model
from flask_appbuilder.models.decorators import renders
# Import SQLAlchemy components for defining the model schema and relationships.
from sqlalchemy import Column, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship

# Import custom base models and helpers from the application.
from myapp.models.base import MyappModelBase
from myapp.models.helpers import AuditMixinNullable
# It seems there is a missing import for 'ImportMixin' and 'conf', which are used later.
# from flask_appbuilder.models.mixins import ImportMixin
# from myapp.app import app
# conf = app.config

# Get the metadata object from the base Model class. Metadata stores information about the database schema.
metadata = Model.metadata


# Define the ETL_Pipeline class, which maps to the 'etl_pipeline' table.
# This model represents an ETL (Extract, Transform, Load) pipeline.
class ETL_Pipeline(Model, AuditMixinNullable, MyappModelBase):
    # Specifies the name of the database table for this model.
    __tablename__ = 'etl_pipeline'
    # Defines the primary key column for the table.
    id = Column(Integer, primary_key=True)
    # Defines a column for the unique name of the pipeline.
    name = Column(String(100), nullable=False, unique=True)
    # Defines a column for a human-readable description of the pipeline.
    describe = Column(String(200), nullable=False)
    # Defines a foreign key column that links to the 'project' table.
    project_id = Column(Integer, ForeignKey('project.id'), nullable=False)  # 定义外键
    # Defines a many-to-one relationship to the 'Project' model.
    project = relationship('Project', foreign_keys=[project_id])
    # Defines a column to store the pipeline's structure as a JSON string, representing a Directed Acyclic Graph (DAG).
    dag_json = Column(Text(65536), nullable=True, default='{}')  # pipeline的上下游关系
    # Defines a column to store global configuration settings for the pipeline, likely as a JSON string.
    config = Column(Text(65536), default='{}')  # pipeline的全局配置
    # Defines a column for storing extra or expanded information, likely as a JSON string.
    expand = Column(Text(65536), default='[]')

    # This line is commented out, but it would have been used by F.A.B.'s import/export feature.
    # export_children = "etl_task"

    # Defines the official string representation of an ETL_Pipeline instance.
    def __repr__(self):
        return self.name

    # This property generates an HTML link to the pipeline's web interface.
    @property
    def etl_pipeline_url(self):
        """
        Creates a clickable HTML link to the web view of this specific ETL pipeline.
        """
        pipeline_url = '/etl_pipeline_modelview/web/' + str(self.id)
        return Markup(f'<a target=_blank href="{pipeline_url}">{self.describe}</a>')

    # This method is decorated with @renders to customize how the 'dag_json' field is displayed in FAB views.
    @renders('dag_json')
    def dag_json_html(self):
        """
        Renders the 'dag_json' field as a preformatted code block for better readability in the UI.
        """
        dag_json = self.dag_json or '{}'
        return Markup('<pre><code>' + dag_json + '</code></pre>')

    # This method renders the 'config' field as a preformatted code block.
    @renders('config')
    def config_html(self):
        """
        Renders the 'config' JSON as a preformatted code block.
        """
        config = self.config or '{}'
        return Markup('<pre><code>' + config + '</code></pre>')

    # This method renders the 'expand' field as a preformatted code block.
    @renders('expand')
    def expand_html(self):
        """
        Renders the 'expand' JSON as a preformatted code block.
        """
        return Markup('<pre><code>' + self.expand + '</code></pre>')
    
    # This method appears to be rendering a 'parameter' field which is not defined in the model.
    # This might be a leftover from a previous version or a typo.
    @renders('parameter')
    def parameter_html(self):
        """
        Renders a 'parameter' attribute as a preformatted code block.
        Note: 'self.parameter' is not defined in this model's columns.
        """
        return Markup('<pre><code>' + self.parameter + '</code></pre>')

    # This property generates an HTML link to view task instances for this pipeline.
    @property
    def run_instance(self):
        """
        Creates a link to the task instance view, using a URL from the application configuration.
        """
        # 'conf' is not defined in this scope. It should be imported.
        url_path = conf.get('MODEL_URLS', {}).get('etl_task_instance')
        # print(url)
        return Markup(f"<a target=_blank href='{url_path}'>任务实例</a>")

    # This method creates a new, unsaved copy of the current pipeline instance.
    def clone(self):
        """
        Creates a deep copy of the pipeline object, useful for templating or duplication.
        The name is slightly modified to ensure uniqueness.
        """
        return ETL_Pipeline(
            name=self.name.replace('_', '-'),
            describe=self.describe,
            project_id=self.project_id,
            dag_json=self.dag_json,
            config=self.config,
            expand=self.expand,
        )


# Define the ETL_Task class, which maps to the 'etl_task' table.
# This model represents a single task within an ETL pipeline.
# 'ImportMixin' is not defined in the provided code, it's likely a F.A.B. mixin for data import/export.
class ETL_Task(Model, AuditMixinNullable, MyappModelBase):
    # Specifies the name of the database table for this model.
    __tablename__ = 'etl_task'
    # Defines the primary key column for the table.
    id = Column(Integer, primary_key=True)
    # Defines a column for the unique name of the task.
    name = Column(String(100), nullable=False, unique=True)
    # Defines a column for a human-readable description of the task.
    describe = Column(String(200), nullable=False)
    # Defines a foreign key column that links this task to its parent pipeline.
    etl_pipeline_id = Column(Integer, ForeignKey('etl_pipeline.id'), nullable=False)  # 定义外键
    # Defines a many-to-one relationship to the 'ETL_Pipeline' model.
    etl_pipeline = relationship('ETL_Pipeline', foreign_keys=[etl_pipeline_id])
    # Defines a column for the name of the template used by this task.
    template = Column(String(100), nullable=False)
    # Defines a column to store task-specific arguments, likely as a JSON string.
    task_args = Column(Text(65536), default='{}')
    # Defines a column for a unique identifier for the task, perhaps used by the execution engine.
    etl_task_id = Column(String(100), nullable=False)
    # Defines a column for storing extra or expanded information.
    expand = Column(Text(65536), default='[]')

    # This line is commented out, but it would have been used by F.A.B.'s import/export feature.
    # export_parent = "etl_pipeline"

    # Defines the official string representation of an ETL_Task instance.
    def __repr__(self):
        return self.name

    # This property generates an HTML link to the task's parent pipeline.
    @property
    def etl_pipeline_url(self):
        """
        Creates a clickable HTML link to the web view of the parent ETL pipeline.
        """
        pipeline_url = '/etl_pipeline_modelview/web/' + str(self.etl_pipeline.id)
        return Markup(f'<a target=_blank href="{pipeline_url}">{self.etl_pipeline.describe}</a>')
